package my.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.xml.XmlMapper;

public class DispatcherServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;

	private static final ObjectMapper jsonMapper = new ObjectMapper();
	private static final XmlMapper xmlMapper = new XmlMapper();

	private static final Log log = LogFactory.getLog(DispatcherServlet.class);

	@Override
	public void destroy() {
		log.debug(getServletName() + "servlet 销毁 ... ");
		ClassScanUtil.requestMapperMap.clear();
	}

	@Override
	public void init() throws ServletException {
		String packageNameRegx = getServletConfig().getInitParameter("basePackageNameRegx");
		ClassScanUtil cs = new ClassScanUtil();
		cs.setPackageRex(packageNameRegx);
		cs.init();
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		parseRequest(req, resp);
	}

	protected void parseRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		response.setCharacterEncoding("GBK");

		String uri = solveUrl(request);

		RequestMapperBean requestMapper = ClassScanUtil.requestMapperMap.get(uri);
		if (requestMapper == null) {
			response.sendRedirect(request.getContextPath() + "/");
			return;
		}

		try {
			Object object = getMethod(requestMapper).invoke(BeanInstanceManager.getBeanInstance(requestMapper.getClassName()), getParamValues(request, requestMapper, response));
			parseResponseValue(request, response, object);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取请求的参数的值
	 * @param request
	 * @param requestMapper
	 * @return
	 * @throws Exception
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 */
	private Object[] getParamValues(HttpServletRequest request, RequestMapperBean requestMapper, HttpServletResponse response)
			throws Exception, InstantiationException, IllegalAccessException {
		String[] paramNames = requestMapper.getParamNames();
		Object[] paramValues = new Object[paramNames.length];
		for (int i = 0; i < paramValues.length; i++) {

			Class<?> paramType = requestMapper.getParamTypes()[i];

			if (paramType.isAssignableFrom(HttpServletRequest.class)) {
				paramValues[i] = request;
				continue;
			} else if (paramType.isAssignableFrom(HttpServletResponse.class)) {
				paramValues[i] = response;
				continue;
			} else if (paramType.isAssignableFrom(HttpSession.class)) {
				paramValues[i] = request.getSession();
				continue;
			} else if (paramType.isAssignableFrom(ServletConfig.class)) {
				paramValues[i] = getServletConfig();
				continue;
			} else if (paramType.isAssignableFrom(ServletContext.class)) {
				paramValues[i] = getServletContext();
				continue;
			}

			String[] paramValue = request.getParameterValues(paramNames[i]);

			Object paramRealValue = getParamRealValue(paramValue, paramType);

			if (paramRealValue == null) {
				// 这里的参数类型不是基本的数据类型
				Field[] fields = paramType.getDeclaredFields();
				Object paramObject = paramType.newInstance();
				for (Field field : fields) {
					field.setAccessible(true);
					field.set(paramObject, getParamRealValue(request.getParameterValues(field.getName()), field.getType()));
				}
				paramRealValue = paramObject;
			}
			paramValues[i] = paramRealValue;
		}
		return paramValues;
	}

	/**
	 * 获取请求的方法
	 * @param requestMapper
	 * @return
	 * @throws ClassNotFoundException
	 * @throws NoSuchMethodException
	 */
	private Method getMethod(RequestMapperBean requestMapper) throws ClassNotFoundException, NoSuchMethodException {
		Class<?> c = Class.forName(requestMapper.getClassName());
		return c.getDeclaredMethod(requestMapper.getMethodName(), requestMapper.getParamTypes());
	}

	/**
	 * 解析返回的对象
	 * @param request
	 * @param response
	 * @param object
	 * @throws ServletException
	 * @throws IOException
	 * @throws JsonProcessingException
	 * @throws JsonGenerationException
	 * @throws JsonMappingException
	 */
	private void parseResponseValue(HttpServletRequest request, HttpServletResponse response, Object object)
			throws ServletException, IOException, JsonProcessingException, JsonGenerationException, JsonMappingException {
		if (object instanceof PageView) {
			solvePageView(request, response, object);
		} else {
			solveStringView(response, object);
		}
	}

	/**
	 * 解析stringView[json,xml,string]
	 * @param response
	 * @param object
	 * @throws JsonProcessingException
	 * @throws IOException
	 * @throws JsonGenerationException
	 * @throws JsonMappingException
	 */
	private void solveStringView(HttpServletResponse response, Object object) throws JsonProcessingException, IOException, JsonGenerationException, JsonMappingException {
		Object data = "";
		if (object instanceof StringView) {
			StringView view = (StringView) object;
			data = view.getData();
			if (view.getType().equals(StringView.TYPE_VIEW_JOSN)) {
				data = jsonMapper.writeValueAsString(data);
			} else if (view.getType().equals(StringView.TYPE_VIEW_XML)) {
				data = xmlMapper.writeValueAsString(data);
			} else {
				data = data.toString();
			}
		} else {
			data = object == null ? "" : object;
		}
		PrintWriter write = response.getWriter();
		write.write(data.toString());
		write.flush();
		write.close();
	}

	/**
	 * 解析返回到jsp页面的对象
	 * @param request
	 * @param response
	 * @param object
	 * @throws ServletException
	 * @throws IOException
	 */
	private void solvePageView(HttpServletRequest request, HttpServletResponse response, Object object) throws ServletException, IOException {
		PageView view = (PageView) object;
		HashMap<String, Object> attributeMap = view.getAttributeMap();
		for (String key : attributeMap.keySet()) {
			request.setAttribute(key, attributeMap.get(key));
		}
		request.getRequestDispatcher("/" + view.getUrl()).forward(request, response);
	}

	/**
	 * 对获取的url进行解析(去掉网址和后缀,只获取实际映射的部分)
	 * @param request
	 * @return
	 */
	private String solveUrl(HttpServletRequest request) {
		String uri = request.getRequestURI().replaceAll(request.getContextPath(), "");
		// String uri = "/action/sayHello";
		if (uri.indexOf(".") != -1) {
			uri = uri.substring(0, uri.indexOf("."));
		}
		return uri;
	}

	private Object getParamRealValue(String[] paramValue, Class<?> paramType) throws Exception {

		if (paramType.isAssignableFrom(String[].class)) {
			return paramValue;
		}
		boolean paramIsNull = paramValue == null || paramValue.length == 0 || StringUtils.isBlank(paramValue[0]);
		if (paramType.isAssignableFrom(int.class) || paramType.isAssignableFrom(Integer.class)) {
			return paramIsNull ? 0 : Integer.parseInt(paramValue[0]);
		} else if (paramType.isAssignableFrom(float.class) || paramType.isAssignableFrom(Float.class)) {
			return paramIsNull ? 0 : Float.parseFloat(paramValue[0]);
		} else if (paramType.isAssignableFrom(double.class) || paramType.isAssignableFrom(Double.class)) {
			return paramIsNull ? 0 : Double.parseDouble(paramValue[0]);
		} else if (paramType.isAssignableFrom(String.class)) {
			return paramIsNull ? "" : paramValue[0];
		} else if (paramType.isAssignableFrom(Date.class)) {
			return paramIsNull ? null : new SimpleDateFormat("yyyy-MM-dd").parse(paramValue[0]);
		} else {
			return null;
		}
	}

	public static void main(String[] args) {
		/*try {
			TestAction test = TestAction.class.newInstance();
			Method method = TestAction.class.getDeclaredMethod("sayHello", String.class, int.class, String.class);
			method.invoke(test, "guojian", 12, "2017");
		} catch (Exception e) {
			e.printStackTrace();
		}*/

		/*SqlBean sql = new SqlBean();
		sql.setName("guojiansqlBean");
		sql.setAge(100);
		
		String xmlData = "";
		
		try {
			xmlData = xmlMapper.writeValueAsString(sql);
		} catch (JsonGenerationException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println(xmlData);*/

	}
}
